//********************************************************************
//	created:	14:2:2012   18:06
//	filename: 	epollsocketmanager.cpp
//	author:		tiamo
//	purpose:	epoll socket manager
//********************************************************************

#include "stdafx.h"
#include "epollsocketmanager.h"

#ifndef WIN32
namespace network
{
	//
	// add socket
	//
	bool EpollSocketManager::onAddSocket(utils::SmartPointer<AsyncSocket> const& theSocket)
	{
		struct epoll_event epollEvent;
		epollEvent.data.u64													= 0;
		epollEvent.data.fd													= static_cast<int>(theSocket->getHandle());
		epollEvent.events													= EPOLLIN | EPOLLOUT | EPOLLERR | EPOLLET;
		return epoll_ctl(mPollFile, EPOLL_CTL_ADD, static_cast<int>(theSocket->getHandle()), &epollEvent) != -1;
	}

	//
	// remove socket
	//
	void EpollSocketManager::onRemoveSocket(utils::SmartPointer<AsyncSocket> const& theSocket)
	{
		epoll_ctl(mPollFile, EPOLL_CTL_DEL, static_cast<int>(theSocket->getHandle()), nullptr);
	}

	//
	// start
	//
	void EpollSocketManager::start(utils::Config* config)
	{
		mMaxOpenCount														= config->getInt(L"/config/socket_manager/epoll_max", 40960);
		mPollFile															= epoll_create(static_cast<int>(mMaxOpenCount));
		if(mPollFile < 0)
			ThrowException(L"EpollSocketManager(%p): create epoll file failed with error (%d)", this, platform::getLastErrorCode());

		SocketManager::start(config);
	}

	//
	// shutdown
	//
	void EpollSocketManager::shutdown()
	{
		close(mPollFile);
		SocketManager::shutdown();
	}

	//
	// build event set
	//
	std::vector<std::pair<utils::SmartPointer<AsyncSocket>, int>> EpollSocketManager::buildEventSet(struct epoll_event* eventBuffer, int eventCount)
	{
		utils::Locker lock(mSocketListLock);
		std::vector<std::pair<utils::SmartPointer<AsyncSocket>, int>> eventSet;
		for(int i = 0; i < eventCount; i ++, eventBuffer ++)
		{
			auto it															= mSocketList.find(eventBuffer->data.fd);
			if(it == mSocketList.end())
				continue;

			int socketEvent													= eventBuffer->events & (EPOLLIN | EPOLLOUT | EPOLLERR);
			if(socketEvent)
				eventSet.push_back(std::make_pair(it->second, socketEvent));
		}
		return eventSet;
	}

	//
	// poll network event
	//
	void EpollSocketManager::pollNetworkEvent(uint32_t pollTimeout)
	{
		//
		// flush socket buffer
		//
		flushSocketBuffer();

		//
		// poll all events
		//
		mEventBuffer.resize(getSocketCount() * sizeof(struct epoll_event));
		auto eventBuffer													= static_cast<struct epoll_event*>(mEventBuffer.getBuffer());
		int maxCount														= static_cast<int>(mEventBuffer.getLength() / sizeof(struct epoll_event));
		int eventCount														= epoll_wait(mPollFile, eventBuffer, maxCount, pollTimeout);
		if(eventCount < 0)
			return;

		//
		// build event set
		//
		auto eventSet														= buildEventSet(eventBuffer, eventCount);

		//
		// call socket
		//
		auto callSocket = [this](std::pair<utils::SmartPointer<AsyncSocket>, int>& theEvent)
		{
			onNetworkEvent(theEvent.first, !!(theEvent.second & EPOLLIN), !!(theEvent.second & EPOLLOUT), !!(theEvent.second & EPOLLERR));
		};
		std::for_each(eventSet.begin(), eventSet.end(), callSocket);
	}
}
#endif
